import 'dart:async';

import 'package:dio/dio.dart';
import 'package:tx_live_plugin_example/http/param_interceptor.dart';
import 'package:tx_live_plugin_example/http/url_path.dart';
import 'package:tx_live_plugin_example/util/logic_presenter.dart';
import 'package:tx_live_plugin_example/util/toast_utils.dart';

///请求失败或异常的回调方法
typedef MsgCallback = bool Function(ResponseBody?);

///请求成功回调方法
typedef OnValueCallback = FutureOr? Function(dynamic value);

///当前app的请求客户端
class HttpClient {
  static const CODE_OK = 0;
  static const ERR_NETWORK = -1;
  static const ERR_CODE = -2;
  static final HttpClient _client = HttpClient._();
  late Dio _dio;

  ///重定向构造方法
  HttpClient._() {
    int time = 30 * 1000;
    _dio = Dio(BaseOptions(
      baseUrl: api,
      connectTimeout: time,
      receiveTimeout: time,
      sendTimeout: time,
    ));
    _dio.interceptors.add(ParamInterceptor());
    _dio.interceptors.add(LogInterceptor(
        request: false, requestHeader: false, responseBody: envTest));
  }

  ///get请求
  static Future<dynamic> get(String url,
      {OnValueCallback? onValue,
      Map<String, dynamic>? params,
      MsgCallback? onMsg}) {
    return _client._dio
        .get(url, queryParameters: params)
        .then((value) => _client.transformResponse(value, onMsg),
            onError: (e) => _client.transformError(e, onMsg))
        .then((value) => _handleValue(value, onValue), onError: _onError);
  }

  static Future<dynamic> post(String url,
      {Map<String, dynamic>? params,
      MsgCallback? onMsg,
      OnValueCallback? onValue}) {
    return _client._dio
        .post(url, queryParameters: params)
        .then((v) => _client.transformResponse(v, onMsg),
            onError: (e) => _client.transformError(e, onMsg))
        .then((v) => _handleValue(v, onValue), onError: _onError);
  }

  static Future<dynamic> postBody(String url,
      {Map<String, dynamic>? params,
      MsgCallback? onMsg,
      OnValueCallback? onValue}) {
    return _client._dio
        .post(url, data: params)
        .then((v) => _client.transformResponse(v, onMsg),
            onError: (e) => _client.transformError(e, onMsg))
        .then((v) => _handleValue(v, onValue), onError: _onError);
  }

  ///上传文件
  //var formData = FormData.fromMap({
  //   'name': 'wendux',
  //   'age': 25,
  //   'file': await MultipartFile.fromFile('./text.txt', filename: 'upload.txt'),
  //   'files': [
  //     await MultipartFile.fromFile('./text1.txt', filename: 'text1.txt'),
  //     await MultipartFile.fromFile('./text2.txt', filename: 'text2.txt'),
  //   ]
  // });
  // var response = await dio.post('/info', data: formData);
  static Future<dynamic> upload(String urlPath,
      {dynamic data,
      Map<String, dynamic>? params,
      ProgressCallback? onSendProgress,
      MsgCallback? onMsg,
      OnValueCallback? onValue}) {
    Options options = Options(
      contentType: "multipart/form-data",
    );
    return _client._dio
        .post(urlPath,
            queryParameters: params,
            data: data,
            options: options,
            onSendProgress: onSendProgress)
        .then((v) => _client.transformResponse(v, onMsg),
            onError: (e) => _client.transformError(e, onMsg))
        .then((v) => _handleValue(v, onValue), onError: _onError);
  }

  ///请求结果转换
  Future<ResponseBody> transformResponse(Response? value, MsgCallback? onMsg) {
    ResponseBody body = ResponseBody();
    dynamic data = value?.data;
    if (data != null) {
      // && data is Map,分类接口返回是数组，不是map
      //当前请求，只要正常返回，默认都是请求成功
      body.code = _getCode(data);
      body.msg = _getMessage(data);
      body.data = _getData(data);
    }
    if (body.isSuccess) return Future.value(body);
    _error(body, onMsg);
    return Future.error(body);
  }

  ///请求异常转换
  void transformError(Exception? err, MsgCallback? onMsg) {
    ResponseBody doby = ResponseBody();
    doby.code = ERR_NETWORK;
    doby.msg = "当前网络不可用，请检查你的网络." + err.toString();
    _error(doby, onMsg, err);
  }

  ///获取，请求结果中的code值
  int _getCode(dynamic data) {
    if (data is! Map) return ERR_CODE;
    var d = data['returnCode'] ?? data['resultCode'];
    if (d == null) return ERR_CODE;
    if (d is String && d.startsWith("E")) d = d.substring(1);
    return d is int ? d : LogicPresenter.parseInt("$d");
  }

  ///获取，请求结果中的消息值
  String _getMessage(dynamic data) {
    if (data is! Map) return '';
    return data["message"] ?? data["resultDesc"] ?? '';
  }

  ///获取，请求结果中的数据值
  dynamic _getData(dynamic data) {
    if (data is! Map) return data;
    return data["resultData"] ?? data;
  }

  ///请求过程中的异常提示处理
  void _error(ResponseBody? body, MsgCallback? onMsg, [Exception? err]) {
    // if (body != null && body.code == HttpClient.LOGIN_OUT) {
    //   BuildContext context = navigatorKey.currentState!.overlay!.context;
    //   FDialog.showCommonDialog(context,
    //       title: "强制退出",
    //       positiveText: "我知道了",
    //       content: "登录已过期或在已其他设备上登录，请重新登录。若非本人操作，请及时改密码。",
    //       isOutSideClickCancel: false,
    //       isSingleBtn: true, positiveFun: () {
    //         AppUtils.startActivityClearOther(
    //             context: context, activity: Login(clearLogin: true));
    //       });
    //    return;
    // }
    bool b = onMsg == null ? true : onMsg(body);
    print("------toast $b, http error $err, body = $body");
    if (b && body?.msg != null) {
      print("------http toast ${body?.msg}");
      ToastUtils.toast(body!.msg!);
    }
  }

  ///请求最终错误  不予处理
  static _onError(d) {
    //nothing to do
    //请求错误时报这个，运行正常，不知哪里触发：http _onError = type 'Null' is not a subtype of type 'FutureOr<ResponseBody<dynamic>>'
    print("http _onError = $d");
  }

  ///请求成功的处理
  ///经过transformError处理后，value可能为空
  static _handleValue(dynamic value, OnValueCallback? onValue) {
    if (value == null || value is! ResponseBody) {
      print("http _handleValue  is no ResponseBody");
      return;
    }
    ResponseBody body = value;
    if (body.isSuccess) {
      print("http _handleValue");
      //需要根据返回数据处理
      return onValue != null ? onValue(body.data) : body.data;
    }
  }
}

///请求结果封装，dio依赖已有同名称的类名ResponseBody，注意区分
class ResponseBody<T> {
  int? code;
  String? msg;
  T? data;

  @override
  String toString() {
    return 'ResponseBody{code: $code, msg: $msg, data: $data}';
  }

  ///返回是否请求成功
  bool get isSuccess => code == HttpClient.CODE_OK;
}

///顶层函数，只要导入该文件，可直接调用该方法
///判断data是否含有key，有则取值，没有则返回默认value
dynamic opt(Map<String, dynamic>? data, String key, {dynamic value}) {
  return data != null && data.containsKey(key) ? data[key] : value;
}
